#include "BTreeNode.h"
#include <iostream>

using namespace std;

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::read(PageId pid, const PageFile& pf)
{
    RC ret = pf.read(pid, (void*)&this->buffer);
    return ret;
}

/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::write(PageId pid, PageFile& pf)
{
    RC ret = pf.write(pid, (const void*)&this->buffer);
    return ret;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNode::getKeyCount()
{
    return this->buffer.numKeys;
}

void BTNode::init()
{
    this->buffer.numKeys = 0;
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNode::insert(int key, const RecordId& rid)
{
    RC ret;
    if (this->buffer.numKeys == MAXKEYS)
    {
        ret = RC_NODE_FULL;
    } else {
        int eid;
        RecordKeyPair temp1, temp2;
        RC result = this->locate(key, eid);
        if (result == RC_NO_SUCH_RECORD)
            eid = this->buffer.numKeys;
        temp1 = this->buffer.pairs[eid];
        for (int i = eid+1; i < this->buffer.numKeys+2; i++)
        {
            temp2 = this->buffer.pairs[i];
            this->buffer.pairs[i] = temp1;
            temp1 = temp2;
        }

        this->buffer.pairs[eid].rid = rid;
        this->buffer.pairs[eid].key = key;
        this->buffer.numKeys++;
        ret = 0;
    }
    return ret;
}

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::insertAndSplit(int key, const RecordId& rid, 
                              BTNode& sibling)
{
    RC ret;
	//split node first
	//do a memcpy of the second half of the array to the new sibling
	int firstHalfN = this->buffer.numKeys / 2;
	int secondHalfN = this->buffer.numKeys - firstHalfN;
	RecordKeyPair *from = this->buffer.pairs + firstHalfN;
	RecordKeyPair *to = sibling.buffer.pairs;
	size_t numBytes = secondHalfN * sizeof(RecordKeyPair);
	memcpy((void*)to, (const void*)from, numBytes);
	this->buffer.numKeys = firstHalfN;
	sibling.buffer.numKeys = secondHalfN;

	//then see which half the new key will belong in, and insert it accordingly.
	if(key >= sibling.buffer.pairs[0].key)
		ret = sibling.insert(key, rid);
	else
		ret = this->insert(key, rid);
	//TODO: also, do we need to set the sibling pointers? think after writing those functions.
	//(but probably)
    return ret;
}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::locate(int searchKey, int& eid)
{
    for (RC i = 0; i < this->buffer.numKeys; i++)
    {
        if (this->buffer.pairs[i].key >= searchKey)
        {
            eid = i;
            return 0;
        }
    }
    return RC_NO_SUCH_RECORD;
}

void BTNode::print()
{
    for (size_t i = 0; i < this->buffer.numKeys; i++)
    {
        cout << ":::Node #" << this->buffer.pairs[i].key << ":::" <<  endl;
        cout << "pid: " << this->buffer.pairs[i].rid.pid << " sid: " << this->buffer.pairs[i].rid.sid << endl << endl;
    }
}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNode::readEntry(int eid, int& key, RecordId& rid)
{
	//TODO think about maybe making this check the caller's responsibility
    if (eid > this->buffer.numKeys && eid != MAXPOINTERS-1)
        return RC_NO_SUCH_RECORD;
    key = this->buffer.pairs[eid].key;
    rid = this->buffer.pairs[eid].rid;
    return 0;
}

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, 
                              BTLeafNode& sibling, int& siblingKey)
{
	RC ret;
	ret = BTNode::insertAndSplit(key, rid, sibling);
	if(ret !=0)
		return ret;
	RecordId temp;
	ret = sibling.readEntry(0, siblingKey, temp);
	return ret;
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{ 
	return this->buffer.pairs[this->getKeyCount()].rid.pid;
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{ 
	this->buffer.pairs[this->getKeyCount()].rid.pid = pid;
	return 0;
}

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{ 
	RC ret;
    RecordId rid;
    rid.pid = pid;
	ret = BTNode::insertAndSplit(key, rid, sibling);
	RecordId temp;
	readEntry(this->getKeyCount()-1, midKey, temp);
	this->buffer.numKeys --;
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{ 
	//can just call locate (once we create Node class as our parent class) and then return the pid from the record.
	RC ret;
	int eid;
	int key;
	RecordId rid;
	ret = locate(searchKey, eid);
	if (ret == RC_NO_SUCH_RECORD){
		//it's past the end, so return the last pointer (one past the last key).
		pid = this->buffer.pairs[this->getKeyCount()].rid.pid;
		return 0;
	}
	ret = readEntry(eid, key, rid);
	pid = rid.pid;
	return ret; 
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{
    this->buffer.numKeys = 1;
	this->buffer.pairs[0].key = key;
	this->buffer.pairs[0].rid.pid = pid1;
	this->buffer.pairs[1].rid.pid = pid2;
	return 0;
}
